#include <stdlib.h>
#include <jni.h>

#include "CLogcat.h"
#include "prc_ctrl.h"

JavaVM *g_VM;
jobject jCallback;

jmethodID onBufJavaCallbackId;
jmethodID onKeyEventJavaCallbackId;

process_callback_p mProcessCallback;

static int onProcessBuffer(unsigned char *data, int len) {
    //LOGD("onProcessNearBuffer %d", len);

    JNIEnv *env;
    int getEnvStat = (*g_VM)->GetEnv(g_VM, (void **) &env, JNI_VERSION_1_6);
    if (getEnvStat == JNI_EDETACHED) {
        if ((*g_VM)->AttachCurrentThread(g_VM, &env, NULL) != 0) {
            return -1;
        }
    }

    jbyteArray jby = (*env)->NewByteArray(env, len);
    (*env)->SetByteArrayRegion(env, jby, 0, len, (const jbyte *) data);
    (*env)->CallVoidMethod(env, jCallback, onBufJavaCallbackId, jby);
    (*env)->DeleteLocalRef(env, jby);
    return 0;
}

static int onProcessKeyEvent(int keycode, int value) {
    LOGI("onProcessKeyEvent keycode = %d, value = %d ", keycode, value);
    JNIEnv *env;
    int getEnvStat = (*g_VM)->GetEnv(g_VM, (void **) &env, JNI_VERSION_1_6);
    if (getEnvStat == JNI_EDETACHED) {
        if ((*g_VM)->AttachCurrentThread(g_VM, &env, NULL) != 0) {
            return -1;
        }
    }

    (*env)->CallVoidMethod(env, jCallback, onKeyEventJavaCallbackId, keycode, value);
    return 0;
}

JNIEXPORT jint JNICALL
Java_com_aispeech_rcprvd_jni_RcNativeProcessJni_native_1device_1state(JNIEnv *env,
                                                                      jclass clazz) {
    return get_device_state();
}

JNIEXPORT jint JNICALL
Java_com_aispeech_rcprvd_jni_RcNativeProcessJni_native_1process_1init(JNIEnv *env, jclass clazz,
                                                                      jobject listener) {
    (*env)->GetJavaVM(env, &g_VM);
    jCallback = (void *) (*env)->NewGlobalRef(env, listener);

    if (!jCallback) {
        LOGD("jcallback is null !!");
        return -1;
    }

    jclass jcbClass = (*env)->GetObjectClass(env, jCallback);
    if (!jcbClass) {
        LOGD("Unable to find call back class");
        (*g_VM)->DetachCurrentThread(g_VM);
        return -2;
    }

    //获取要回调的方法ID
    onBufJavaCallbackId = (*env)->GetMethodID(env, jcbClass, "onBuffer", "([B)V");
    onKeyEventJavaCallbackId = (*env)->GetMethodID(env, jcbClass, "onKeyEvent", "(II)V");

    if (!onBufJavaCallbackId || !onKeyEventJavaCallbackId) {
        LOGD("Unable to find method for callback id");
        return -3;
    }

    //将回调函数传给process control
    mProcessCallback = calloc(1, sizeof(process_callback_t));
    if (!mProcessCallback) {
        LOGD("Unable to calloc process_callback_t");
        free(mProcessCallback);
        mProcessCallback = NULL;
        return -4;
    }

    mProcessCallback->onProcessBuffer = onProcessBuffer;
    mProcessCallback->onProcessKeyEvent = onProcessKeyEvent;

    init_pctrl(mProcessCallback);

    return 0;
}

JNIEXPORT jint JNICALL
Java_com_aispeech_rcprvd_jni_RcNativeProcessJni_native_1process_1start(JNIEnv *env, jclass clazz,
                                                                       jint id) {
    LOGD("process_start %d", id);
    return start_pctrl(id);
}

JNIEXPORT jint JNICALL
Java_com_aispeech_rcprvd_jni_RcNativeProcessJni_native_1process_1feed(JNIEnv *env, jclass clazz,
                                                                      jint id, jbyteArray data,
                                                                      jint len) {

    jbyte *jni_rec_buf = (*env)->GetByteArrayElements(env, data, 0);
    int ret = do_process(id, (unsigned char *) jni_rec_buf, len);
    (*env)->ReleaseByteArrayElements(env, data, jni_rec_buf, JNI_ABORT);
    return ret;
}

JNIEXPORT jint JNICALL
Java_com_aispeech_rcprvd_jni_RcNativeProcessJni_native_1process_1stop(JNIEnv *env, jclass clazz,
                                                                      jint id) {
    LOGD("process_stop %d", id);
    return stop_pctrl(id);
}

JNIEXPORT jint JNICALL
Java_com_aispeech_rcprvd_jni_RcNativeProcessJni_native_1process_1destroy(JNIEnv *env,
                                                                         jclass clazz) {
    LOGD("process_destroy");
    destroy_pctrl();

    if (mProcessCallback) {
        free(mProcessCallback);
        mProcessCallback = NULL;
    }
    return 0;
}